# -*-coding:utf-8-*-
import pandas as pd
import matplotlib.pyplot as plt

data: pd.DataFrame = pd.read_excel(r'..\book.xlsx', sheet_name=4)

"""
pd.qcut(x, q, labels=None, retbins=False, precision=3, duplicates="raise")
    按数据个数平均分配到指定的组数中，每组数据个数相同、区间不同。
    返回series或tuple(series, bins)，其中series数据类型为Categorical或整型（labels=False）
    -x：要进行分箱的数据，数据类型为一维数组
    -q：组数，即要将数据分成几组，区间是左开右闭
    -labels：箱标签，标签个数要和组数相等。如果为False，生成整数类型series（数值为元素位于第几组）
    -retbins：False时返回series，True时返回tuple(series, bins)（bins为ndarray类型的分组边界值）
    -precision：存储和显示箱标签的精度。
    duplicates：如果bin边缘不是唯一的，则引发ValueError（参数为raise）或删除非唯一的（参数为drop）
>>> qcut1
0     3
1     3
...
24    0
25    2
Name: score, dtype: int64
>>> qcut2
0      (69.25, 99.0]
1      (69.25, 99.0]
...
24    (21.999, 33.0]
25     (52.0, 69.25]
Name: score, dtype: category
Categories (4, interval[float64, right]): [(21.999, 33.0] < (33.0, 52.0] < (52.0, 69.25] < (69.25, 99.0]]
>>> qcut3
0     组4
1     组4
...
24    组1
25    组3
Name: score, dtype: category
Categories (4, object): ['组1' < '组2' < '组3' < '组4']
"""
qcut1 = pd.qcut(data['score'], 4, labels=False)  # 分成四组，labels=False返回整数类型series
qcut2 = pd.qcut(data['score'], 4)  # 分成四组，返回Categorical类型series
qcut3 = pd.qcut(data['score'], 4, labels=['组1', '组2', '组3', '组4'])  # 指定组标签

"""
pd.cut(x, bins, right=True, labels=None, retbins=False, precision=3,
       include_lowest=False, duplicates="raise", ordered=True)
    按给定的区间将数据分配到相应的组中
    返回series或tuple(series, bins)，其中series数据类型为Categorical或整型（labels=False）
    -x：要分箱的数组。必须是一维的
    -bins：分类的标准。 
        * int：将x的区间平均分成指定的段数。x的范围在每一侧扩展了0.1%，以包括x的最小值和最大值
        * 标量序列：定义允许非均匀宽度的bin边缘，没有扩展x的范围
        * IntervalIndex：定义要使用的确切箱，bins的IntervalIndex必须不重叠
    -right：True：分组区间左开右闭。False：分组区间左闭右开
    -labels：箱标签，标签个数要和组数相等。
        * False：生成整数类型series（数值为元素位于第几组）
        * 当bins是IntervalIndex时，忽略此参数
        * 当ordered=False时，必须提供标签。
    -retbins：False时返回series，True时返回tuple(series, bins)（bins为ndarray类型的分组边界值）
    -precision：存储和显示箱标签的精度。
    -include_lowest：第一个间隔是否应该是左包含的。
    -duplicates：如果bin边缘不是唯一的，则引发ValueError（参数为raise）或删除非唯一的（参数为drop）
    -ordered：标签是否有序。适用于返回的类型为Series。True：对生成的分类进行排序。False：生成的分类无序（必须提供标签）
>>> cut1
0       (60.5, 79.75]
1       (79.75, 99.0]
...
24    (21.923, 41.25]
25      (41.25, 60.5]
Name: score, dtype: category
Categories (4, interval[float64, right]): [(21.923, 41.25] < (41.25, 60.5] < (60.5, 79.75] < (79.75, 99.0]]
>>> cut2
0     2
1     3
...
24    0
25    1
Name: score, dtype: int64
>>> cut3
0      及格
1      优秀
...
24    不及格
25    不及格
Name: score, dtype: category
Categories (4, object): ['不及格' < '及格' < '良好' < '优秀']
>>> counts
不及格    18
及格      3
良好      1
优秀      4
Name: score, dtype: int64
"""
cut1 = pd.cut(data['score'], bins=4)  # 根据最高和最低分，将分数区间平均分为4段
cut2 = pd.cut(data['score'], bins=4, labels=False)
cut3 = pd.cut(data['score'],
              bins=[0, 60, 80, 90, 100+1],
              right=False,
              labels=['不及格', '及格', '良好', '优秀'])  # 左闭右开，最后需要加个1保证能统计到刚好为100的分数
counts = pd.value_counts(cut3, sort=False)  # 统计出各组的数量，返回series

# 生成统计图表
plt.rcParams['font.sans-serif'] = ['KaiTi']
plt.rcParams['axes.unicode_minus'] = False
bar = plt.bar(counts.index.astype('object'), counts)
plt.title('成绩分段直方图')
plt.xlabel('成绩段')
plt.ylabel('人数')
plt.bar_label(bar, counts)
plt.show()
